package initialize

import (
	"bbs125Check/consts"
	"bbs125Check/kayleeDiskLog"
	"context"
	"fmt"
	"github.com/kaylee595/tracerr"
	"github.com/lmittmann/tint"
	"github.com/samber/lo"
	slogmulti "github.com/samber/slog-multi"
	"io"
	"log/slog"
	"os"
	"time"
)

func initLogger() {
	// 1.彩色日志输出到控制台和文件 consts.ColorLogFilename
	h1 := tint.NewHandler(io.MultiWriter(os.Stdout, newKayleeDiskLog(consts.ColorLogFilename)), &tint.Options{
		AddSource:   false,
		Level:       slog.LevelInfo,
		ReplaceAttr: colorErr,
		TimeFormat:  time.DateTime,
	})
	// 2.普通日志info以上级别输出到logs/log.gteInfo.log
	h2 := slog.NewTextHandler(newKayleeDiskLog("logs/log.gteInfo.log"), &slog.HandlerOptions{
		AddSource:   true,
		Level:       slog.LevelInfo,
		ReplaceAttr: traceErr,
	})
	// 2.普通日志debug级别输出到logs/log.eqDebug.log
	debugHandler := slog.NewTextHandler(newKayleeDiskLog("logs/log.eqDebug.log"), &slog.HandlerOptions{
		AddSource:   true,
		Level:       slog.LevelDebug,
		ReplaceAttr: traceErr,
	})
	h3 := slogmulti.Router().Add(debugHandler, matchLevel(slog.LevelDebug)).Handler()

	slog.SetDefault(slog.New(slogmulti.Fanout(h1, h2, h3)))
}

func newKayleeDiskLog(filename string) *kayleeDiskLog.Handler {
	return kayleeDiskLog.NewHandler(filename, 0, 0)
}

func colorErr(_ []string, attr slog.Attr) slog.Attr {
	if attr.Key == "err" {
		if err, ok := attr.Value.Any().(error); ok {
			return tint.Err(err)
		}
	}
	return attr
}

func traceErr(_ []string, attr slog.Attr) slog.Attr {
	if attr.Key == "err" {
		te, ok := attr.Value.Any().(tracerr.Error)
		if ok {
			traces := lo.Reduce(te.StackTrace(), func(agg string, item tracerr.Frame, index int) string {
				if len(agg) > 0 {
					agg += " <- "
				}
				return agg + fmt.Sprintf("%s:%d(%s)", item.Path, item.Line, item.Func)
			}, "")
			attr.Value = slog.GroupValue(
				slog.String("msg", te.Error()),
				slog.String("traces", traces),
			)
		}
	}
	return attr
}

func matchLevel(leveler slog.Leveler) func(ctx context.Context, r slog.Record) bool {
	return func(_ context.Context, r slog.Record) bool { return r.Level == leveler.Level() }
}
